package org.summerframework.component.security.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.summerframework.component.security.annotion.MenuRequired;
import org.summerframework.component.security.annotion.MenuRequireds;
import org.summerframework.component.security.annotion.RoleRequired;
import org.summerframework.component.security.events.PermissionsUpdateEvent;
import org.summerframework.component.security.permissions.service.UserPermissionsService;
import org.summerframework.component.security.permissions.service.dto.UserPermissionsDTO;
import org.summerframework.component.security.security.service.DefaultRoleService;
import org.summerframework.core.base.result.DefaultResultCode;
import org.summerframework.core.exception.WebException;
import org.summerframework.core.util.SpringContextUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static org.summerframework.component.security.core.interceptor.SecurityInterceptor.ADMIN;
import static org.summerframework.component.security.core.interceptor.SecurityInterceptor.ANY;

/**
 * @author 石超
 * @version v1.0.0
 */
@Configuration
@ConditionalOnProperty(name = "application.menu", havingValue = "true")
public class MenuConfiguration implements ApplicationListener<ApplicationStartedEvent> {
    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private UserPermissionsService userPermissionsService;

    @Resource
    private ObjectMapper objectMapper;

    @Value("${spring.sysn.url:#{null}}")
    private String url;

    @Resource
    private DefaultRoleService defaultRoleService;

    private void selectByParentId(List<UserPermissionsDTO> list, Long parentId) {
        List<UserPermissionsDTO> permissionsList = userPermissionsService.selectByParentId(parentId);

        list.addAll(permissionsList);

        if (CollectionUtils.isNotEmpty(permissionsList)) {
            permissionsList.forEach(permissions -> selectByParentId(list, permissions.getId()));
        }
    }

    @Async
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(MenuRequireds.class);
        RequestMappingHandlerMapping mappings = applicationContext.getBean(RequestMappingHandlerMapping.class);
        HashMap<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodHashMap = new HashMap<>(mappings.getHandlerMethods());

        transactionTemplate.execute(transactionStatus -> {
            try {
                DefaultRoleService.THREAD_LOCAL.set(false);
                map.forEach((key, bean) -> {
                    MenuRequireds menuRequireds = AnnotationUtils.findAnnotation(bean.getClass(), MenuRequireds.class);
                    List<UserPermissionsDTO> userPermissionsDTOS = new ArrayList<>();
                    long parentId = menuRequireds.parentId();

                    if (parentId == 0) {
                        parentId = 1L;
                    }

                    selectByParentId(userPermissionsDTOS, parentId);

                    List<String> codes = userPermissionsDTOS.stream().map(UserPermissionsDTO::getCode).collect(Collectors.toList());
                    List<String> addCodes = new ArrayList<>();

                    AtomicInteger atomicInteger = new AtomicInteger(menuRequireds.sortIndex());

                    List<String> parentCodes = Arrays.stream(menuRequireds.value()).map(MenuRequired::code).collect(Collectors.toList());
                    Map<String, Long> parentIds = new HashMap<>();

                    //先添加顶级
                    long finalParentId = parentId;
                    Arrays.stream(menuRequireds.value()).forEach(menuRequired -> {
                        UserPermissionsDTO userPermissionsDTO = new UserPermissionsDTO();
                        userPermissionsDTO.setCode(menuRequired.code());
                        userPermissionsDTO.setName(menuRequired.name());
                        int soreIndex = menuRequired.soreIndex();

                        if (soreIndex < 0) {
                            soreIndex = 0;
                        }

                        userPermissionsDTO.setSortIndex(atomicInteger.get() >= 0 ? atomicInteger.getAndIncrement() : soreIndex);
                        userPermissionsDTO.setType(1);
                        userPermissionsDTO.setParentId(finalParentId);
                        userPermissionsDTO.setUrl(menuRequired.url());
                        userPermissionsDTO.setIcon(menuRequired.icon());
                        userPermissionsDTO.setIsSystem(1);
                        userPermissionsDTO.setCreateBy(1L);
                        userPermissionsDTO.setUpdateBy(1L);
                        userPermissionsService.insert(userPermissionsDTO);
                        parentIds.put(menuRequired.code(), userPermissionsDTO.getId());
                        codes.remove(menuRequired.code());
                        addCodes.add(menuRequired.code());

                    });

                    //子集
                    Arrays.stream(menuRequireds.value()).forEach(menuRequired -> {
                        if (menuRequired.beanClass().length > 0) {
                            Arrays.stream(menuRequired.beanClass()).forEach(beanClass -> {
                                AtomicInteger sortIndex = new AtomicInteger(0);

                                requestMappingInfoHandlerMethodHashMap.forEach((requestMappingInfo, handlerMethod) -> {
                                    if (handlerMethod.getBeanType() == beanClass) {
                                        Method method = handlerMethod.getMethod();

                                        AtomicInteger ai = new AtomicInteger(0);
                                        RoleRequired roleRequired1 = AnnotationUtils.findAnnotation(method, RoleRequired.class);
                                        RoleRequired.List list = AnnotationUtils.findAnnotation(method, RoleRequired.List.class);

                                        Consumer<RoleRequired> consumer = roleRequired -> {
                                            if (roleRequired == null) return;

                                            boolean isAny = Arrays.asList(roleRequired.code()).contains(ANY);
                                            AtomicInteger i = new AtomicInteger(0);

                                            Arrays.stream(roleRequired.code()).forEach(role -> {
                                                if (StringUtils.equals(role, ADMIN) || StringUtils.equals(role, ANY) || parentCodes.contains(role) || addCodes.contains(role))
                                                    return;
                                                AtomicInteger roleCode = new AtomicInteger(0);
                                                UserPermissionsDTO interfaceUserPermissionsDTO = new UserPermissionsDTO();
                                                interfaceUserPermissionsDTO.setCode(role);
                                                int count = ai.getAndIncrement();
                                                interfaceUserPermissionsDTO.setName(roleRequired.name().length > roleCode.get() ? roleRequired.name()[roleCode.get()] : "自己改");
                                                interfaceUserPermissionsDTO.setSortIndex(sortIndex.getAndIncrement());
                                                interfaceUserPermissionsDTO.setType(2);
                                                interfaceUserPermissionsDTO.setUrl(new ArrayList<>(requestMappingInfo.getPatternsCondition().getPatterns()).get(0));
                                                if (StringUtils.isNotBlank(roleRequired.parentCode())) {
                                                    if (parentIds.get(roleRequired.parentCode()) != null) {
                                                        interfaceUserPermissionsDTO.setParentId(parentIds.get(roleRequired.parentCode()));
                                                    } else {
                                                        throw new WebException(DefaultResultCode.ILLEGAL_ARGUMENT, roleRequired.parentCode() + "不在顶级中");
                                                    }
                                                } else {
                                                    interfaceUserPermissionsDTO.setParentId(parentIds.get(menuRequired.code()));
                                                }

                                                try {
                                                    interfaceUserPermissionsDTO.setRequestMappingInfo(objectMapper.writeValueAsString(requestMappingInfo));
                                                } catch (JsonProcessingException e) {
                                                    e.printStackTrace();
                                                }

                                                interfaceUserPermissionsDTO.setIsSystem(1);
                                                interfaceUserPermissionsDTO.setCreateBy(1L);
                                                interfaceUserPermissionsDTO.setUpdateBy(1L);

                                                userPermissionsService.insert(interfaceUserPermissionsDTO);
                                                codes.remove(role);
                                                if (roleRequired.name().length > count) {
                                                    addCodes.add(menuRequired.code());
                                                }
                                                i.incrementAndGet();
                                                roleCode.incrementAndGet();
                                            });
                                        };

                                        if (list != null) {
                                            Arrays.stream(list.value()).forEach(consumer);
                                        } else {
                                            consumer.accept(roleRequired1);
                                        }
                                    }
                                });
                            });
                        }
                    });

                    userPermissionsDTOS.stream().filter(permissions -> codes.contains(permissions.getCode())).map(UserPermissionsDTO::getId).sorted(Comparator.reverseOrder()).forEach(userPermissionsService::deleteById);
                });


                defaultRoleService.keys().forEach(key -> {
                    String userId = key.replace(DefaultRoleService.ROLE_SAVE_KEY, "");
                    defaultRoleService.parse(userId);
                    SpringContextUtil.publishEvent(new PermissionsUpdateEvent(userId, PermissionsUpdateEvent.PermissionsTypeEnum.ALL));
                });
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                e.printStackTrace();
            }

            return null;
        });

//        if (StringUtils.isNotBlank(url)) {
//            SysnUtil.sysn(url + UserController.action + "?type=1");
//            SysnUtil.sysn(url + SysOrganController.action + "?type=2");
//        }


        System.out.println("完成");
    }
}

